﻿Option Explicit On
Imports System.ComponentModel
Imports System.Data.OleDb
Imports System.Runtime.InteropServices
Imports System.Text
Imports ClosedXML.Excel
Imports Microsoft.Office.Interop.Access

Public Class EIQ_AIモデル前処理F
#Region "画面処理"
    ' ========================================
    ' フォームロード
    ' ========================================
    Private Sub EIQ_AIモデル前処理F_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' デザイナー表示時に実行するとエラーになる処理は実行しない
        If LicenseManager.UsageMode = LicenseUsageMode.Runtime Then
            著作L.Text = 著作
            Tera計算Data確認()
            'MsgBox("Tera計算AIは出荷データ")
            T200テーブル確認()
        End If
        ' 先にグローバル例外ハンドラを設定
        SetupGlobalExceptionHandlers()

        Try
            著作L.Text = 著作

            ' ファイル確認・Excel 読込などは例外捕捉してログ出力する
            Try
                Tera計算Data確認()
            Catch ex As Exception
                LogStartupError(ex, "Tera計算Data確認")
            End Try

            Try
                LoadExcelDataSimple()
            Catch ex As Exception
                LogStartupError(ex, "LoadExcelDataSimple()")
            End Try

            変更削除表示L.Text = "変更削除 不可"
        Catch ex As Exception
            ' ここに来るのは想定外。ログを残す。
            LogStartupError(ex, "EIQ_AIモデル作成F_Load (outer)")
        End Try
    End Sub
    '===================================
    'モデル作成方法Tp
    '===================================
    '===================================
    '  出荷データから作成Tp
    '===================================
    Private Sub 出荷データから作成Bt_Click_1(sender As Object, e As EventArgs) Handles 出荷データから作成Bt.Click
        ' EIQ_AIモデル作成フォームを開く
        Dim form As New EIQ_AIモデル前処理F()
        form.メインCt.SelectedTab = form.出荷データから作成Tp
        form.Show()
    End Sub
    Private Sub Access新規作成Bt_Click_1(sender As Object, e As EventArgs) Handles Access新規作成Bt.Click
        Access新規作成()
        Accessインポート()
        T110_区分換算()
        T200作成_EIQデータ加工()
        全出荷GPLランク()

        メモTb.Text = "乱数 追加中" ： メモTb.BackColor = Color.PaleTurquoise : Me.Refresh()

        乱数追加()
        メモTb.Text = "乱数 追加終了" ： メモTb.BackColor = Color.PaleTurquoise : Me.Refresh()

        メモTb.Text = "出荷データから T200作成 完了 閉じてください" ： メモTb.BackColor = Color.PaleTurquoise : Me.Refresh()
        MsgBox("出荷データから T200作成 完了。 閉じてください")

        Access新規作成メモTb.Text = ""
        AccessインポートメモTb.Text = ""
        T110_区分換算メモTb.Text = ""
        T200table作成メモTb.Text = ""
        全出荷GPLランク作成メモTb.Text = ""

    End Sub
    Private Sub ユーザー設定Cb_CheckedChanged(sender As Object, e As EventArgs) Handles ユーザー設定Cb.CheckedChanged
        TpChk1 = 0
        画面Ct.SelectedTab = 計算ルール設定Tp
    End Sub
    '===================================
    '  Tera計算ccdbにアタッチTp
    '===================================
    Private Sub Tera計算ccdbにアタッチTb_Click_1(sender As Object, e As EventArgs) Handles Tera計算ccdbにアタッチTb.Click
        ' EIQ_AIモデル作成フォームを開く
        Dim form As New EIQ_AIモデル前処理F()
        form.メインCt.SelectedTab = form.T200から作成TP
        form.Show()
    End Sub


    Private Sub E1_閉じるTb_Click(sender As Object, e As EventArgs) Handles E1_閉じるTb.Click
        Me.Close()
    End Sub

#End Region

#Region "EIQ_AIモデル前処理"
    ' ========================================
    ' データベース確認
    ' ========================================
    Private Sub T200テーブル確認()
        Dim dbPath As String = Tera計算DataPath & "\EIQ分析AI化\EIQ_AIモデル.accdb"
        Dim connectionString As String = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath};"

        ' データベースファイルの存在確認
        If Not System.IO.File.Exists(dbPath) Then
            Accdbの状況Tb.Text = "EIQ_AIモデル.accdb が存在しません。"
            Return
        End If

        Try
            Using cn As New OleDbConnection(connectionString)
                cn.Open()

                ' テーブル一覧を取得
                Dim schema As DataTable = cn.GetSchema("Tables")
                Dim t200Exists As Boolean = False
                Dim tableType As String = ""

                For Each row As DataRow In schema.Rows
                    If row("TABLE_NAME").ToString() = "T200" Then
                        t200Exists = True
                        tableType = row("TABLE_TYPE").ToString()
                        Exit For
                    End If
                Next

                If t200Exists Then
                    ' T200のレコード数を確認
                    Dim sql As String = "SELECT COUNT(*) FROM T200"
                    Using cmd As New OleDbCommand(sql, cn)
                        Dim recordCount As Integer = Convert.ToInt32(cmd.ExecuteScalar())

                        ' テーブルタイプを判別
                        Dim typeDisplay As String = ""
                        If tableType = "LINK" OrElse tableType.Contains("LINKED") Then
                            typeDisplay = "「Tera計算.accdbにアタッチ」で作成されてる。"
                        Else
                            typeDisplay = "「出荷データ」から作成されてる。"
                        End If

                        Accdbの状況Tb.Text = $"T200テーブルが存在します。 モデル作成のための前処理(この画面)の必要は無い。{vbCrLf}{vbCrLf}" & $"種類: {typeDisplay}" &
                                             $"レコード数: {recordCount}{vbCrLf}{vbCrLf}" &
                                             $"出荷データや計算ルールの変更なければ、閉じるボタンクリック。「2.AIモデルデータ設定」へ進む。"


                    End Using
                Else
                    Accdbの状況Tb.Text = "T200テーブルが存在しません。「AIモデルデータ作成」の準備が出来てません" & vbCrLf &
                                          "「出荷データから作成」または「Tera計算.accdbにアタッチ」を実行してください。"
                End If
            End Using
        Catch ex As Exception
            Accdbの状況Tb.Text = $"データベース確認エラー: {ex.Message}"
        End Try
    End Sub

#Region "出荷データから作成"

    ' Global exception handlers とログ
    Private Sub SetupGlobalExceptionHandlers()
        Try
            AddHandler System.Windows.Forms.Application.ThreadException, Sub(sender, e)
                                                                             Try
                                                                                 LogStartupError(e.Exception, "Application.ThreadException")
                                                                                 MessageBox.Show("起動時エラーが発生しました。詳細はログを確認してください。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
                                                                             Catch
                                                                             End Try
                                                                         End Sub

            AddHandler AppDomain.CurrentDomain.UnhandledException, Sub(sender, e)
                                                                       Try
                                                                           Dim ex As Exception = TryCast(e.ExceptionObject, Exception)
                                                                           LogStartupError(ex, "AppDomain.UnhandledException")
                                                                           MessageBox.Show("重大な起動エラーが発生しました。詳細はログを確認してください。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
                                                                       Catch
                                                                       End Try
                                                                   End Sub
        Catch
            ' 設定失敗は無視（極稀）
        End Try
    End Sub

    Private Sub LogStartupError(ex As Exception, context As String)
        Try
            Dim logFile = IO.Path.Combine(Tera計算DataPath, "EIQ_startup_error.log")
            Dim sb As New StringBuilder()
            sb.AppendLine("----- " & DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") & " -----")
            sb.AppendLine("Context: " & context)
            If ex IsNot Nothing Then
                sb.AppendLine("Exception.Type: " & ex.GetType().FullName)
                sb.AppendLine("Message: " & ex.Message)
                sb.AppendLine("StackTrace: " & ex.StackTrace)
                If ex.InnerException IsNot Nothing Then
                    sb.AppendLine("InnerException: " & ex.InnerException.Message)
                    sb.AppendLine(ex.InnerException.StackTrace)
                End If
            Else
                sb.AppendLine("Exception = NULL")
            End If
            IO.File.AppendAllText(logFile, sb.ToString(), Encoding.UTF8)
        Catch
            ' ログも失敗したら諦める（最小化）
        End Try
    End Sub

    ' COM 解放ユーティリティ
    Private Sub ReleaseCom(obj As Object)
        If obj Is Nothing Then Return
        Try
            While Marshal.FinalReleaseComObject(obj) > 0
                ' ループして完全解放
            End While
        Catch
            Try
                Marshal.ReleaseComObject(obj)
            Catch
            End Try
        Finally
            obj = Nothing
            GC.Collect()
            GC.WaitForPendingFinalizers()
        End Try
    End Sub


#Region " 出荷データ読込 T100_Dataテーブル作成"

    Private Sub Access新規作成()
        Dim manager As New AccessDbManager()

        MyPath = manager.Tera計算Data確認

        If MyPath = "0" Or MyPath = "" Then
            MsgBox("Tera計算Data" & " が見つかりませんでした。" & " 閉じてください。")
            Exit Sub
        End If
        If MyPath > "1" And MyPath < "A" Then
            MsgBox("Tera計算Dataが " & MyPath & "フォルダーあります。" & " 閉じてください。")
            Exit Sub
        End If

        If MyPath < "A" Then Exit Sub

        MyDB = MyPath & "\EIQ分析AI化\EIQ_AIモデル.accdb"
        manager.Db新規作成(MyDB)
        Access新規作成メモTb.Text = MyDB & "Db新規作成完了"
        Access新規作成メモTb.BackColor = Color.Yellow : Me.Refresh()

    End Sub

    Private Sub Accessインポート()
        'MyDb = MyPath & "\EIQ分析AI化\EIQ_AIモデル.accdb"
        MyExcel = MyPath & "\EIQ分析AI化\出荷データ.xlsx"
        Mysheet = "Sheet1"
        Dim manager As New AccessDbManager()

        Dim AA = manager.出荷データインポート(MyDB, MyExcel, Mysheet)

        AccessインポートメモTb.Text = MyDB & "   出荷データインポート完了"
        AccessインポートメモTb.BackColor = Color.Yellow : Me.Refresh()
    End Sub


    Private Sub T110_区分換算()
        T100_Dataテーブル作成()
        T110_区分換算メモTb.Text = "T100_区分換算テーブル作成"
        T110_区分換算テーブル作成()
        各換算計算()
        T110_区分換算メモTb.Text = "各換算計算作成"
        出荷単位区分()
        T110_区分換算メモTb.Text = "T110_区分換算 完了"
        T110_区分換算メモTb.BackColor = Color.Yellow : Me.Refresh()

    End Sub


    Private Sub 全出荷GPLランク()
        全出荷GPLランク作成メモTb.Text = "70% 全出荷GPLランク作成"
        全出荷GPLランク作成メモTb.BackColor = Color.Yellow : Me.Refresh()
        全出荷GPLランク作成()
        全出荷GPLランク作成メモTb.Text = "T200列追加"
        全出荷GPLランク作成メモTb.BackColor = Color.LightCyan : Me.Refresh()
        T200列追加()
        全出荷GPLランク作成メモTb.Text = "T200列追加処理終了"
        全出荷GPLランク作成メモTb.BackColor = Color.LavenderBlush : Me.Refresh()
    End Sub
    Private Sub 全出荷GPLランク作成()

        Access新規作成メモTb.Text = "80% データ加工中" ： Access新規作成メモTb.BackColor = Color.Yellow : Me.Refresh()
        My_DATA読込() '  1.条件読込My_DATA1

        '  1.テーブルが有れば削除
        DEL_table("T160*")

        Dim Mytable1 As Object
        Dim 出荷方法, ランクキー, IE As String

        '＝＝追加
        出荷方法 = "全出荷" : ランクキー = "GPL"
        Dim 全出荷ランクキー As String = "GPL"

        Mytable1 = "T160_全出荷E集計" : IE = "出荷先" : ランク記号 = 全出荷ランクキー & "E"
        ランク作成2(出荷方法, Mytable1, IE, ランク記号, ランクキー)
        Mytable1 = "T160_全出荷I集計" : IE = "アイテム" : ランク記号 = 全出荷ランクキー & "I"
        ランク作成2(出荷方法, Mytable1, IE, ランク記号, ランクキー)

        '＝＝


    End Sub
    Private Sub ランク作成2(出荷方法 As String, Mytable1 As Object, IE As String, ランク記号 As String, ランクキー As String)

        '  2." & MyTable1 & "テーブル作成
        Dim sqlCommands As New List(Of String)
        '順位
        sqlCommands.Add("SELECT  T100_Data." & IE & ", """ & ランクキー & """ AS ランクキー, Sum([T110_区分換算]![" & ランクキー & "]) AS キー INTO " & Mytable1 & " FROM  T100_Data LEFT JOIN T110_区分換算  ON T100_Data.行連番 = T110_区分換算.行連番 GROUP BY  T100_Data." & IE & ", """ & ランクキー & """  ORDER BY Sum([T110_区分換算]![" & ランクキー & "]) DESC;")
        sqlCommands.Add("ALTER TABLE " & Mytable1 & " ADD COLUMN 順位 COUNTER")

        '累計
        sqlCommands.Add("ALTER Table " & Mytable1 & " Add 累計 FLOAT")
        sqlCommands.Add("UPDATE " & Mytable1 & " Set " & Mytable1 & ".累計 = DSum(""キー"",""" & Mytable1 & """,""順位<="" & [順位])*1;")

        '累計比＝累計比／累計最大
        sqlCommands.Add("ALTER Table " & Mytable1 & " Add 累計比 FLOAT")
        sqlCommands.Add("Select Max(" & Mytable1 & ".累計) As 累計の最大 INTO T001 FROM " & Mytable1 & ";")
        sqlCommands.Add("UPDATE " & Mytable1 & ", T001 Set " & Mytable1 & ".累計比 = [" & Mytable1 & "]![累計]/[T001]![累計の最大];")
        sqlCommands.Add("Drop table T001")

        'ランク
        Dim A1 As Double = Double.Parse(ランクA1) / 100
        Dim A2 As Double = Double.Parse(ランクA2) / 100 + A1
        Dim B As Double = Double.Parse(ランクB) / 100 + A2
        Dim C As Double = Double.Parse(ランクC) / 100 + B
        Dim D As Double = Double.Parse(ランクD) / 100 + C

        sqlCommands.Add("ALTER Table " & Mytable1 & " Add ランク TEXT(10)")
        If Mytable1 = "T160_全出荷E集計" Then
            sqlCommands.Add("UPDATE " & Mytable1 & " Set " & Mytable1 & ".[ランク] = IIf([" & Mytable1 & "]![累計比]<=" & A1 & ",""GPLE_A1"",IIf([" & Mytable1 & "]![累計比]<=" & A2 & ",""GPLE_A2"",IIf([" & Mytable1 & "]![累計比]<=" & B & ",""GPLE_B"",IIf([" & Mytable1 & "]![累計比]<=" & C & ",""GPLE_C"",""GPLE_D""))));")
        End If
        If Mytable1 = "T160_全出荷I集計" Then
            sqlCommands.Add("UPDATE " & Mytable1 & " Set " & Mytable1 & ".[ランク] = IIf([" & Mytable1 & "]![累計比]<=" & A1 & ",""GPLI_A1"",IIf([" & Mytable1 & "]![累計比]<=" & A2 & ",""GPLI_A2"",IIf([" & Mytable1 & "]![累計比]<=" & B & ",""GPLI_B"",IIf([" & Mytable1 & "]![累計比]<=" & C & ",""GPLI_C"",""GPLI_D""))));")
        End If
        Sqlcommands_Add(sqlCommands)

    End Sub
    Private Sub 乱数追加()
        Dim ss As Integer = 0
        Dim tables As String() = {
            "T150_B単位出荷E集計",
            "T150_B単位出荷I集計",
            "T150_C単位出荷E集計",
            "T150_C単位出荷I集計",
            "T160_全出荷E集計",
            "T160_全出荷I集計"
        }

        If String.IsNullOrWhiteSpace(MyDB) Then
            MsgBox("DBパスが未設定のため乱数追加を中止します。")
            Return
        End If

        Me.Invoke(Sub()
                      Access新規作成メモTb.Text = "乱数列追加処理を開始"
                      Access新規作成メモTb.BackColor = Color.Yellow
                      Me.Refresh()
                  End Sub)

        Try
            ' ADOXでテーブル情報を取得（リンク判定のため）
            Dim catalog As New ADOX.Catalog()
            catalog.let_ActiveConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";")

            ' --- 1) まず各テーブルに「乱数」列がなければ追加（可能な限り） ---
            For Each t As String In tables
                If Not Search_Table(t) Then Continue For
                ss += 1
                Try
                    Dim isLinked As Boolean = False
                    Dim remoteDatasource As String = MyDB
                    Dim remoteTableName As String = t

                    For i As Integer = 0 To catalog.Tables.Count - 1
                        If String.Equals(catalog.Tables(i).Name, t, StringComparison.OrdinalIgnoreCase) Then
                            Try
                                Dim props = catalog.Tables(i).Properties
                                If props.Item("Jet OLEDB:Create Link") IsNot Nothing AndAlso
                                   CBool(props.Item("Jet OLEDB:Create Link").Value) = True Then
                                    isLinked = True
                                    Try
                                        remoteDatasource = props.Item("Jet OLEDB:Link Datasource").Value.ToString()
                                    Catch
                                    End Try
                                    Try
                                        remoteTableName = props.Item("Jet OLEDB:Remote Table Name").Value.ToString()
                                    Catch
                                    End Try
                                End If
                            Catch
                                ' プロパティ参照失敗は無視
                            End Try
                            Exit For
                        End If
                    Next

                    Using cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & remoteDatasource & ";")
                        cn.Open()

                        ' スキーマ取得で列存在を確認
                        Dim colExists As Boolean = False
                        Using schemaCmd As New OleDbCommand("SELECT * FROM [" & remoteTableName & "] WHERE 1=0", cn)
                            Using rdr As OleDbDataReader = schemaCmd.ExecuteReader(CommandBehavior.SchemaOnly)
                                Dim dtSchema As DataTable = rdr.GetSchemaTable()
                                If dtSchema IsNot Nothing Then
                                    For Each row As DataRow In dtSchema.Rows
                                        If String.Equals(row("ColumnName").ToString(), "乱数", StringComparison.OrdinalIgnoreCase) Then
                                            colExists = True
                                            Exit For
                                        End If
                                    Next
                                End If
                            End Using
                        End Using

                        If Not colExists Then
                            Try
                                Dim addCmd As New OleDbCommand("ALTER TABLE [" & remoteTableName & "] ADD COLUMN 乱数 DOUBLE", cn)
                                addCmd.ExecuteNonQuery()
                            Catch ex As Exception
                                ' ALTER が失敗する場合はログに出す
                                Me.Invoke(Sub()
                                              Access新規作成メモTb.Text = $"列追加エラー({If(isLinked, "リンク先", "ローカル")}): {t} -> {ex.Message}"
                                              Access新規作成メモTb.BackColor = Color.LightCoral
                                              Me.Refresh()
                                          End Sub)
                                ' 次のテーブルへ
                                メモ4Tb.Text = ss & " テーブル完了" : メモ1Tb.BackColor = Color.Yellow : Me.Refresh()

                                Continue For
                            End Try
                        End If

                        cn.Close()
                    End Using
                Catch ex As Exception
                    Me.Invoke(Sub()
                                  Access新規作成メモTb.Text = "列追加検出エラー: " & t & " - " & ex.Message
                                  Access新規作成メモTb.BackColor = Color.LightCoral
                                  Me.Refresh()
                              End Sub)
                End Try
            Next

            ' --- 2) 乱数割当（順位が無ければ 出荷先/アイテム を使って更新） ---
            ' 再現性のある乱数列にするため固定シードを利用
            ' 必要なら My_DATA の 分析日 や MyDB パスからシードを算出するよう変更可能
            Dim seed As Integer = 123456
            Dim rnd As New Random(seed) ' 固定シード（毎回同じ乱数列）

            For Each t As String In tables
                If Not Search_Table(t) Then Continue For

                ' 再度 ADOX でリンク先を決定
                Dim catalog2 As New ADOX.Catalog()
                catalog2.let_ActiveConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";")
                Dim remoteDatasource As String = MyDB
                Dim remoteTableName As String = t

                For i As Integer = 0 To catalog2.Tables.Count - 1
                    If String.Equals(catalog2.Tables(i).Name, t, StringComparison.OrdinalIgnoreCase) Then
                        Try
                            Dim props = catalog2.Tables(i).Properties
                            If props.Item("Jet OLEDB:Create Link") IsNot Nothing AndAlso
                               CBool(props.Item("Jet OLEDB:Create Link").Value) = True Then
                                Try
                                    remoteDatasource = props.Item("Jet OLEDB:Link Datasource").Value.ToString()
                                Catch
                                End Try
                                Try
                                    remoteTableName = props.Item("Jet OLEDB:Remote Table Name").Value.ToString()
                                Catch
                                End Try
                            End If
                        Catch
                        End Try
                        Exit For
                    End If
                Next

                Try
                    Using cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & remoteDatasource & ";")
                        cn.Open()

                        ' スキーマで利用可能なキー列を判定（順位、出荷先、アイテムの順）
                        Dim hasJuni As Boolean = False, hasDest As Boolean = False, hasItem As Boolean = False
                        Using schemaCmd As New OleDbCommand("SELECT * FROM [" & remoteTableName & "] WHERE 1=0", cn)
                            Using rdr As OleDbDataReader = schemaCmd.ExecuteReader(CommandBehavior.SchemaOnly)
                                Dim dtSchema As DataTable = rdr.GetSchemaTable()
                                If dtSchema IsNot Nothing Then
                                    For Each row As DataRow In dtSchema.Rows
                                        Dim cname = row("ColumnName").ToString()
                                        If String.Equals(cname, "順位", StringComparison.OrdinalIgnoreCase) Then hasJuni = True
                                        If String.Equals(cname, "出荷先", StringComparison.OrdinalIgnoreCase) Then hasDest = True
                                        If String.Equals(cname, "アイテム", StringComparison.OrdinalIgnoreCase) Then hasItem = True
                                    Next
                                End If
                            End Using
                        End Using

                        ' 更新に使う列がない場合はスキップ
                        If Not (hasJuni Or hasDest Or hasItem) Then
                            Continue For
                        End If

                        ' 読み取り用 SQL を組立て
                        Dim selectCols As New List(Of String)
                        If hasJuni Then selectCols.Add("[順位]")
                        If hasDest Then selectCols.Add("[出荷先]")
                        If hasItem Then selectCols.Add("[アイテム]")
                        Dim selSql As String = "SELECT " & String.Join(", ", selectCols.ToArray()) & " FROM [" & remoteTableName & "]"

                        Using selCmd As New OleDbCommand(selSql, cn)
                            Using rdr As OleDbDataReader = selCmd.ExecuteReader()
                                If Not rdr.HasRows Then
                                    Continue For
                                End If

                                ' 更新コマンドを用意（各キー別）
                                Dim updJ As New OleDbCommand("UPDATE [" & remoteTableName & "] SET 乱数 = ? WHERE [順位] = ?", cn)
                                updJ.Parameters.Add(New OleDbParameter("?", OleDbType.Double))
                                updJ.Parameters.Add(New OleDbParameter("?", OleDbType.Integer))

                                Dim updDest As New OleDbCommand("UPDATE [" & remoteTableName & "] SET 乱数 = ? WHERE [出荷先] = ?", cn)
                                updDest.Parameters.Add(New OleDbParameter("?", OleDbType.Double))
                                updDest.Parameters.Add(New OleDbParameter("?", OleDbType.VarChar, 255))

                                Dim updItem As New OleDbCommand("UPDATE [" & remoteTableName & "] SET 乱数 = ? WHERE [アイテム] = ?", cn)
                                updItem.Parameters.Add(New OleDbParameter("?", OleDbType.Double))
                                updItem.Parameters.Add(New OleDbParameter("?", OleDbType.VarChar, 255))

                                Dim trans As OleDb.OleDbTransaction = cn.BeginTransaction()
                                updJ.Transaction = trans
                                updDest.Transaction = trans
                                updItem.Transaction = trans

                                Try
                                    While rdr.Read()
                                        Dim val As Double = rnd.NextDouble()
                                        If hasJuni AndAlso Not IsDBNull(rdr("順位")) Then
                                            Try
                                                updJ.Parameters(0).Value = val
                                                updJ.Parameters(1).Value = Convert.ToInt32(rdr("順位"))
                                                updJ.ExecuteNonQuery()
                                            Catch
                                                ' 個別更新失敗は行を飛ばす
                                            End Try
                                        ElseIf hasDest AndAlso Not IsDBNull(rdr("出荷先")) Then
                                            Try
                                                updDest.Parameters(0).Value = val
                                                updDest.Parameters(1).Value = rdr("出荷先").ToString()
                                                updDest.ExecuteNonQuery()
                                            Catch
                                            End Try
                                        ElseIf hasItem AndAlso Not IsDBNull(rdr("アイテム")) Then
                                            Try
                                                updItem.Parameters(0).Value = val
                                                updItem.Parameters(1).Value = rdr("アイテム").ToString()
                                                updItem.ExecuteNonQuery()
                                            Catch
                                            End Try
                                        End If
                                    End While

                                    trans.Commit()
                                Catch ex As Exception
                                    trans.Rollback()
                                    Throw
                                Finally
                                    updJ.Dispose()
                                    updDest.Dispose()
                                    updItem.Dispose()
                                End Try
                            End Using
                        End Using

                        cn.Close()
                    End Using
                Catch ex As Exception
                    Me.Invoke(Sub()
                                  Access新規作成メモTb.Text = "乱数割当エラー: " & t & " - " & ex.Message
                                  Access新規作成メモTb.BackColor = Color.LightCoral
                                  Me.Refresh()
                              End Sub)
                End Try
            Next

            Me.Invoke(Sub()
                          Access新規作成メモTb.Text = "乱数列追加処理 完了"
                          Access新規作成メモTb.BackColor = Color.LightGreen
                          Me.Refresh()
                      End Sub)
        Catch ex As Exception
            Me.Invoke(Sub()
                          Access新規作成メモTb.Text = "乱数追加エラー: " & ex.Message
                          Access新規作成メモTb.BackColor = Color.LightCoral
                          Me.Refresh()
                      End Sub)
        End Try
    End Sub
#End Region

#Region " T200作成_EIQデータ加工”

    Private Sub T200作成_EIQデータ加工()
        ' UI更新を最小限に
        Me.SuspendLayout()
        Try
            T200table作成メモTb.Text = "EIQデータ加工を開始しました"
            T200table作成メモTb.BackColor = Color.GreenYellow
            ' Me.Refresh() を削除 - 不要な再描画を防ぐ

            My_DATAテーブル作成()
            EIQ分析用データ作成()

            T200table作成メモTb.Text = "加工データの作成 完了"
            T200table作成メモTb.BackColor = Color.Yellow
        Finally
            Me.ResumeLayout()
            Me.Refresh() ' 最後に1回だけ
        End Try
    End Sub
    Sub T200列追加()
        Dim sqlCommands As New List(Of String)
        sqlCommands.Add("ALTER TABLE T160_全出荷E集計 ALTER COLUMN 順位 NUMBER")
        sqlCommands.Add("ALTER TABLE T160_全出荷I集計 ALTER COLUMN 順位 NUMBER")
        Sqlcommands_Add(sqlCommands)

        Dim sqlCommands2 As New List(Of String)
        Dim Mytable1 = "T200"
        DEL_table("T190*")

        sqlCommands2.Add("SELECT T200.*, [T160_全出荷E集計]![順位] AS GPLE順位, [T160_全出荷E集計]![ランク] AS GPLEランク, [T160_全出荷I集計]![順位] AS GPLI順位, [T160_全出荷I集計]![ランク] AS GPLIランク INTO T190 FROM (T200 LEFT JOIN T160_全出荷E集計 ON T200.出荷先 = T160_全出荷E集計.出荷先) LEFT JOIN T160_全出荷I集計 ON T200.[アイテム] = T160_全出荷I集計.[アイテム]; ")
        sqlCommands2.Add("DROP TABLE T200")
        sqlCommands2.Add("Select * into T200 from T190")
        sqlCommands2.Add("DROP TABLE T190")
        Sqlcommands_Add(sqlCommands2)

    End Sub

#End Region
#End Region

#Region " EIQ分析用データ作成8｢MyData1｣に基づき"
    Private Sub EIQ分析用データ作成()

        Access新規作成メモTb.Text = "EIQデータ加工を開始しました" ： T200table作成メモTb.BackColor = Color.Yellow : Me.Refresh()

        ' My_DATA読込() '  1.条件読込My_DATA1

        '  1.テーブルが有れば削除
        DEL_table("T150*")
        DEL_table("T160*")
        DEL_table("T180*")
        DEL_table("T190*")
        DEL_table("T200*")

        'ランク作成Sqlcommands_Add(New List(Of String))
        Dim Mytable1 As Object
        Dim 出荷方法, ランクキー, IE As String


        T200table作成メモTb.Text = "T150 Table作成 前処理 ケース出荷 作成中" ： T200table作成メモTb.BackColor = Color.Thistle : Me.Refresh()

        'Dim ケース出荷ランクキー = "ケ"
        'Dim バラ出荷ランクキー = "行"

        出荷方法 = "ケース単位出荷" : ランクキー = ケース出荷ランクキー


        Mytable1 = "T150_C単位出荷E集計" : IE = "出荷先" : ランク記号 = ケース出荷ランクキー & "E" : ランク作成(出荷方法, Mytable1, IE, ランク記号, ランクキー)
        Mytable1 = "T150_C単位出荷I集計" : IE = "アイテム" : ランク記号 = ケース出荷ランクキー & "I" : ランク作成(出荷方法, Mytable1, IE, ランク記号, ランクキー)


        T200table作成メモTb.Text = "T150 Table作成 前処理 バラ出荷 作成中" ： T200table作成メモTb.BackColor = Color.LightSteelBlue : Me.Refresh()

        出荷方法 = "バラ単位出荷" : ランクキー = バラ出荷ランクキー
        Mytable1 = "T150_B単位出荷E集計" : IE = "出荷先" : ランク記号 = バラ出荷ランクキー & "E" : ランク作成(出荷方法, Mytable1, IE, ランク記号, ランクキー)
        Mytable1 = "T150_B単位出荷I集計" : IE = "アイテム" : ランク記号 = バラ出荷ランクキー & "I" : ランク作成(出荷方法, Mytable1, IE, ランク記号, ランクキー)


        T200table作成メモTb.Text = "40% T200 Table作成 前処理 ランク 作成中" ： T200table作成メモTb.BackColor = Color.Thistle : Me.Refresh()
        T180_EIQランク_テーブル作成()
        T180_EIQランク組込()        '#

        T200table作成メモTb.Text = "60% T200Table 作成中" ： T200table作成メモTb.BackColor = Color.Aquamarine : Me.Refresh()

        T200Table作成()               'EIQ分析のための加工データを一つのテーブル(T200)にまとめる


        T200table作成メモTb.BackColor = Color.Yellow : T200table作成メモTb.Text = "各種集計2" : Me.Refresh()
        '各種集計2()

    End Sub

#End Region

#Region " T200Table作成"
    Private Sub ランク作成(出荷方法 As String, Mytable1 As Object, IE As String, ランク記号 As String, ランクキー As String)
        '  2." & MyTable1 & "テーブル作成
        Dim sqlCommands As New List(Of String)
        '順位
        sqlCommands.Add("SELECT  T100_Data." & IE & ", """ & ランクキー & """ AS ランクキー, Sum([T110_区分換算]![" & ランクキー & "]) AS キー INTO " & Mytable1 & " FROM  T100_Data LEFT JOIN T110_区分換算  ON T100_Data.行連番 = T110_区分換算.行連番 GROUP BY  T100_Data." & IE & ", """ & ランクキー & """  ORDER BY Sum([T110_区分換算]![" & ランクキー & "]) DESC;")
        sqlCommands.Add("ALTER TABLE " & Mytable1 & " ADD COLUMN 順位 COUNTER")

        '累計
        sqlCommands.Add("ALTER Table " & Mytable1 & " Add 累計 FLOAT")
        sqlCommands.Add("UPDATE " & Mytable1 & " Set " & Mytable1 & ".累計 = DSum(""キー"",""" & Mytable1 & """,""順位<="" & [順位])*1;")

        '累計比＝累計比／累計最大
        sqlCommands.Add("ALTER Table " & Mytable1 & " Add 累計比 FLOAT")
        sqlCommands.Add("Select Max(" & Mytable1 & ".累計) As 累計の最大 INTO T001 FROM " & Mytable1 & ";")
        sqlCommands.Add("UPDATE " & Mytable1 & ", T001 Set " & Mytable1 & ".累計比 = [" & Mytable1 & "]![累計]/[T001]![累計の最大];")
        sqlCommands.Add("Drop table T001")

        'ランク
        Dim A1 As Double = CDbl(ランクA1) / 100
        Dim A2 As Double = CDbl(ランクA2) / 100 + A1
        Dim B As Double = CDbl(ランクB) / 100 + A2
        Dim C As Double = CDbl(ランクC) / 100 + B
        Dim D As Double = CDbl(ランクD) / 100 + C

        sqlCommands.Add("ALTER Table " & Mytable1 & " Add ランク TEXT(10)")
        'TextBox2.Text = ("ALTER Table " & Mytable1 & " Add ランク TEXT(10)")
        sqlCommands.Add("UPDATE " & Mytable1 & " Set " & Mytable1 & ".[ランク] = IIf([" & Mytable1 & "]![累計比]<=" & A1 & ",""A1"",IIf([" & Mytable1 & "]![累計比]<=" & A2 & ",""A2"",IIf([" & Mytable1 & "]![累計比]<=" & B & ",""B"",IIf([" & Mytable1 & "]![累計比]<=" & C & ",""C"",""D""))));")

        Sqlcommands_Add(sqlCommands)
    End Sub


    Sub T180_EIQランク_テーブル作成()
        Dim sqlCommands As New List(Of String)
        'sqlCommands.Add("CREATE TABLE T180_EIQランク(行連番 INT,出荷先 TEXT(20),アイテム TEXT(20),CE順位 INT,CEランク TEXT(10),CI順位 INT,CIランク TEXT(10),BE順位 INT,BEランク TEXT(10),BI順位 INT,BIランク TEXT(10));")
        sqlCommands.Add("CREATE TABLE T180_EIQランク(行連番 INT,出荷先 TEXT(20),アイテム TEXT(20),CE順位 INT,CEランク TEXT(10),CI順位 INT,CIランク TEXT(10),BE順位 INT,BEランク TEXT(10),BI順位 INT,BIランク TEXT(10));")
        Sqlcommands_Add(sqlCommands)
    End Sub
    Private Sub T180_EIQランク組込()
        ' 変数をローカルに保持してSQL文字列の評価回数を削減
        Dim ケースE記号 As String = ケース出荷ランクキー & "E_"
        Dim ケースI記号 As String = ケース出荷ランクキー & "I_"
        Dim バラE記号 As String = バラ出荷ランクキー & "E_"
        Dim バラI記号 As String = バラ出荷ランクキー & "I_"


        Dim sqlCommands As New List(Of String) From {
            "DELETE From T180_EIQランク",
            "INSERT INTO T180_EIQランク ( 行連番, 出荷先, アイテム ) Select T100_Data.行連番, T100_Data.出荷先, T100_Data.[アイテム] FROM T100_Data;",
            $"UPDATE (T180_EIQランク LEFT JOIN T110_区分換算 On T180_EIQランク.行連番 = T110_区分換算.行連番) LEFT JOIN T150_C単位出荷E集計 On T180_EIQランク.出荷先 = T150_C単位出荷E集計.出荷先 Set T180_EIQランク.CE順位 = [T150_C単位出荷E集計]![順位], T180_EIQランク.CEランク = '{ケースE記号}' & RTrim([T150_C単位出荷E集計]![ランク]) WHERE (((T110_区分換算.C行)>0));",
            $"UPDATE (T180_EIQランク LEFT JOIN T110_区分換算 On T180_EIQランク.行連番 = T110_区分換算.行連番) LEFT JOIN T150_C単位出荷I集計 On T180_EIQランク.アイテム = T150_C単位出荷I集計.アイテム Set T180_EIQランク.CI順位 = [T150_C単位出荷I集計]![順位], T180_EIQランク.CIランク = '{ケースI記号}' & RTrim([T150_C単位出荷I集計]![ランク]) WHERE (((T110_区分換算.C行)>0));",
            $"UPDATE (T180_EIQランク LEFT JOIN T110_区分換算 On T180_EIQランク.行連番 = T110_区分換算.行連番) LEFT JOIN T150_B単位出荷E集計 On T180_EIQランク.出荷先 = T150_B単位出荷E集計.出荷先 Set T180_EIQランク.BE順位 = [T150_B単位出荷E集計]![順位], T180_EIQランク.BEランク = '{バラE記号}' & RTrim([T150_B単位出荷E集計]![ランク]) WHERE (((T110_区分換算.B行)>0));",
            $"UPDATE (T180_EIQランク LEFT JOIN T110_区分換算 On T180_EIQランク.行連番 = T110_区分換算.行連番) LEFT JOIN T150_B単位出荷I集計 On T180_EIQランク.アイテム = T150_B単位出荷I集計.アイテム Set T180_EIQランク.BI順位 = [T150_B単位出荷I集計]![順位], T180_EIQランク.BIランク = '{バラI記号}' & RTrim([T150_B単位出荷I集計]![ランク]) WHERE (((T110_区分換算.B行)>0));"
        }
        Sqlcommands_Add(sqlCommands)
    End Sub


    Sub T200Table作成()
        Dim sqlCommands As New List(Of String) From {
            "Select T100_Data.*, T110_区分換算.[ダミー], T110_区分換算.出荷単位, T110_区分換算.C行, T110_区分換算.Cバ, T110_区分換算.Cケ, T110_区分換算.CPL, T110_区分換算.C容, T110_区分換算.C重, T110_区分換算.B行, T110_区分換算.Bバ, T110_区分換算.Bケ, T110_区分換算.BPL, T110_区分換算.B容, T110_区分換算.B重, T110_区分換算.G行, T110_区分換算.Gバ, T110_区分換算.Gケ, T110_区分換算.GPL, T110_区分換算.G容, T110_区分換算.G重, T180_EIQランク.CE順位, T180_EIQランク.CEランク, T180_EIQランク.CI順位, T180_EIQランク.CIランク, T180_EIQランク.BE順位, T180_EIQランク.BEランク, T180_EIQランク.BI順位, T180_EIQランク.BIランク INTO T200 FROM (T100_Data LEFT JOIN T110_区分換算 On T100_Data.行連番 = T110_区分換算.行連番) LEFT JOIN T180_EIQランク On T100_Data.行連番 = T180_EIQランク.行連番;"
        }
        Sqlcommands_Add(sqlCommands)
    End Sub

#End Region

    Sub T100_Dataテーブル作成()
        MySQL = "SELECT T000_出荷データ.*, * INTO T100_Data FROM T000_出荷データ;"
        SQL実行(MySQL)
    End Sub
    Public Sub SQL実行(ByVal MySql As String)

        Dim con As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";")
        Dim cmd As New OleDb.OleDbCommand
        Dim returnValue As Object

        ' cmd.CommandText = "Select * INTO T200_EIQ加工データ1 FROM T110_区分換算;"
        cmd.CommandText = MySql
        cmd.CommandType = CommandType.Text
        cmd.Connection = con

        con.Open()
        returnValue = cmd.ExecuteScalar()

        'MsgBox(returnValue)

        con.Close()
    End Sub


    Sub T110_区分換算テーブル作成()
        Access新規作成メモTb.Text = ""
        Dim sb As New System.Text.StringBuilder
        sb.AppendLine("CREATE TABLE T110_区分換算 (")
        sb.AppendLine("  行連番 INT PRIMARY KEY,")
        sb.AppendLine("  ダミー TEXT(1),")
        sb.AppendLine("  出荷単位 TEXT(14),")
        sb.AppendLine("  C行 FLOAT, Cバ FLOAT, Cケ FLOAT, CPL FLOAT, C容 FLOAT, C重 FLOAT,")
        sb.AppendLine("  B行 FLOAT, Bバ FLOAT, Bケ FLOAT, BPL FLOAT, B容 FLOAT, B重 FLOAT,")
        sb.AppendLine("  G行 FLOAT, Gバ FLOAT, Gケ FLOAT, GPL FLOAT, G容 FLOAT, G重 FLOAT,")
        sb.AppendLine("  保管設備 FLOAT,")
        sb.AppendLine("  出荷設備 FLOAT")
        sb.AppendLine(");")

        SQL実行(sb.ToString())

        ' インデックス追加で JOIN 高速化
        Dim sqlCommands As New List(Of String) From {
            "CREATE INDEX idx_行連番 ON T110_区分換算(行連番);",
            "CREATE INDEX idx_出荷単位 ON T110_区分換算(出荷単位);"
        }
        Sqlcommands_Add(sqlCommands)

        Access新規作成メモTb.Text = "T110_区分換算テーブル作成が完成"
    End Sub

    Sub 各換算計算()
        Dim sqlCommands As New List(Of String)

        ' 1. 一旦T110を空にする（UPDATEよりDELETE+INSERTの方が圧倒的に速いため）
        sqlCommands.Add("DELETE FROM T110_区分換算;")

        ' 2. T100から計算結果を生成しながら一括でINSERTする
        ' INNER JOINで結合し、1つのSQLですべての計算（G, C, B）を完了させる
        Dim sb As New System.Text.StringBuilder()
        sb.AppendLine("INSERT INTO T110_区分換算 ( 行連番, Gバ, Gケ, GPL, G容, G重, Cバ, Cケ, CPL, C容, C重, Bバ, Bケ, BPL, B容, B重 )")
        sb.AppendLine("SELECT ")
        sb.AppendLine("  D.行連番,")
        ' --- 行単位(G) ---
        sb.AppendLine("  D.バラ数, D.バラ数/D.ケース入数, (D.バラ数/D.ケース入数)/D.PLケース積付数, (D.バラ数/D.ケース入数)*D.ケース容積, (D.バラ数/D.ケース入数)*D.ケース重量,")
        ' --- ケース単位(C) ---
        sb.AppendLine("  Int(D.バラ数/D.ケース入数)*D.ケース入数, Int(D.バラ数/D.ケース入数), (Int(D.バラ数/D.ケース入数))/D.PLケース積付数, Int(D.バラ数/D.ケース入数)*D.ケース容積, Int(D.バラ数/D.ケース入数)*D.ケース重量,")
        ' --- バラ単位(B) ---
        sb.AppendLine("  D.バラ数-(Int(D.バラ数/D.ケース入数)*D.ケース入数),") 'Bバ
        sb.AppendLine("  (D.バラ数-(Int(D.バラ数/D.ケース入数)*D.ケース入数))/D.ケース入数,") 'Bケ
        sb.AppendLine("  ((D.バラ数-(Int(D.バラ数/D.ケース入数)*D.ケース入数))/D.ケース入数)/D.PLケース積付数,") 'BPL
        sb.AppendLine("  ((D.バラ数-(Int(D.バラ数/D.ケース入数)*D.ケース入数))/D.ケース入数)*D.ケース容積,") 'B容
        sb.AppendLine("  ((D.バラ数-(Int(D.バラ数/D.ケース入数)*D.ケース入数))/D.ケース入数)*D.ケース重量") 'B重
        sb.AppendLine("FROM T100_Data AS D;")

        sqlCommands.Add(sb.ToString())

        ' 実行用メソッドの呼び出し
        Sqlcommands_Add(sqlCommands)
    End Sub
    Public Sub Sqlcommands_Add(sqlCommands As List(Of String))
        Dim cn As System.Data.OleDb.OleDbConnection =
                  New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";")
        Dim Com As System.Data.OleDb.OleDbCommand = New OleDb.OleDbCommand()
        Com.Connection = cn
        cn.Open()
        Com.Transaction = cn.BeginTransaction
        Try
            Dim i1 As Integer
            For i1 = 0 To sqlCommands.Count - 1
                Com.CommandText = sqlCommands(i1)
                Com.ExecuteNonQuery()
            Next
            Com.Transaction.Commit()
        Catch ex As Exception
            MessageBox.Show(ex.Message)
            Com.Transaction.Rollback()
        Finally
            cn.Close()
        End Try

    End Sub
    Sub 出荷単位区分()
        Dim sqlCommands As New List(Of String)

        ' Switch関数を使用して可読性と効率を上げ、
        ' かつ WHERE句で「更新の必要がある行」だけに絞り込むことで、
        ' 無駄な上書き（全行スキャン）を防ぎます。

        Dim sql As String = "UPDATE T110_区分換算 SET " &
        "C行 = IIf(Cケ>=1, 1, 0), " &
        "B行 = IIf(Bバ>=1, 1, 0), " &
        "G行 = IIf(Cケ>=1 AND Bバ>=1, 2, 1), " &
        "出荷単位 = Switch(" &
            "Cケ>=1 AND Bバ>=1, 'ケースバラ単位出荷', " &
            "Cケ>=1, 'ケース単位出荷', " &
            "True, 'バラ単位出荷'" &
        ");"

        sqlCommands.Add(sql)
        Sqlcommands_Add(sqlCommands)
    End Sub


    Sub ケース出荷バラ出荷区分()
        ' 事前に不要なテーブルを削除
        DEL_table("T120*")

        Dim sqlCommands As New List(Of String)

        ' --- 1. ケース出荷集計の一本化 ---
        ' サブクエリを使って「出荷先数」と「アイテム数・各集計」を同時に計算し、最後に結合します。
        Dim sqlCase As String =
        "SELECT T_Item.出荷日, T_Dest.出荷先数, T_Item.アイテム数, T_Item.行数, T_Item.バラ数, T_Item.ケース換算, T_Item.PL換算, T_Item.容積換算, T_Item.重量換算 " &
        "INTO T120_ケース出荷集計 " &
        "FROM (" &
        "  SELECT 出荷日, Count(アイテム) AS アイテム数, Sum(C行合計) AS 行数, Sum(Cバ合計) AS バラ数, Sum(Cケ合計) AS ケース換算, Sum(CPL合算) AS PL換算, Sum(C容合計) AS 容積換算, Sum(C重合計) AS 重量換算 " &
        "  FROM (SELECT T100.出荷日, T100.アイテム, Sum(T110.C行) AS C行合計, Sum(T110.Cバ) AS Cバ合計, Sum(T110.Cケ) AS Cケ合計, Sum(T110.CPL) AS CPL合算, Sum(T110.C容) AS C容合計, Sum(T110.C重) AS C重合計 " &
        "        FROM T100_Data AS T100 LEFT JOIN T110_区分換算 AS T110 ON T100.行連番 = T110.行連番 " &
        "        GROUP BY T100.出荷日, T100.アイテム HAVING Sum(T110.C行) > 0) GROUP BY 出荷日" &
        ") AS T_Item " &
        "LEFT JOIN (" &
        "  SELECT 出荷日, Count(出荷先) AS 出荷先数 FROM (" &
        "    SELECT T100.出荷日, T100.出荷先 FROM T100_Data AS T100 LEFT JOIN T110_区分換算 AS T110 ON T100.行連番 = T110.行連番 " &
        "    GROUP BY T100.出荷日, T100.出荷先 HAVING Sum(T110.C行) > 0" &
        "  ) GROUP BY 出荷日" &
        ") AS T_Dest ON T_Item.出荷日 = T_Dest.出荷日;"


        ' --- 2. バラ出荷集計の一本化 ---
        Dim sqlBara As String =
        "SELECT T_Item.出荷日, T_Dest.出荷先数, T_Item.アイテム数, T_Item.行数, T_Item.バラ数, T_Item.ケース換算, T_Item.PL換算, T_Item.容積換算, T_Item.重量換算 " &
        "INTO T120_バラ出荷集計 " &
        "FROM (" &
        "  SELECT 出荷日, Count(アイテム) AS アイテム数, Sum(B行合計) AS 行数, Sum(Bバ合計) AS バラ数, Sum(Bケ合計) AS ケース換算, Sum(BPL合算) AS PL換算, Sum(B容合計) AS 容積換算, Sum(B重合計) AS 重量換算 " &
        "  FROM (SELECT T100.出荷日, T100.アイテム, Sum(T110.B行) AS B行合計, Sum(T110.Bバ) AS Bバ合計, Sum(T110.Bケ) AS Bケ合計, Sum(T110.BPL) AS BPL合算, Sum(T110.B容) AS B容合計, Sum(T110.B重) AS B重合計 " &
        "        FROM T100_Data AS T100 LEFT JOIN T110_区分換算 AS T110 ON T100.行連番 = T110.行連番 " &
        "        GROUP BY T100.出荷日, T100.アイテム HAVING Sum(T110.B行) > 0) GROUP BY 出荷日" &
        ") AS T_Item " &
        "LEFT JOIN (" &
        "  SELECT 出荷日, Count(出荷先) AS 出荷先数 FROM (" &
        "    SELECT T100.出荷日, T100.出荷先 FROM T100_Data AS T100 LEFT JOIN T110_区分換算 AS T110 ON T100.行連番 = T110.行連番 " &
        "    GROUP BY T100.出荷日, T100.出荷先 HAVING Sum(T110.B行) > 0" &
        "  ) GROUP BY 出荷日" &
        ") AS T_Dest ON T_Item.出荷日 = T_Dest.出荷日;"


        sqlCommands.Add(sqlCase)
        sqlCommands.Add(sqlBara)
        Sqlcommands_Add(sqlCommands)
    End Sub
#Region "  Access Query・Table削除 サーチ等"
    ' --- ADOX/ADODB 安全ラッパーと置換実装 ---

    Public Sub DEL_QUERY(ByVal Query名 As String)
        Dim cn As ADODB.Connection = Nothing
        Dim cat As ADOX.Catalog = Nothing
        Try
            cn = New ADODB.Connection()
            cn.Open("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";")
            cat = New ADOX.Catalog()
            ' ADODB.Connection オブジェクトを渡す（文字列ではなくオブジェクト）
            cat.let_ActiveConnection(cn)

            Dim h As New List(Of String)
            Dim L = Query名.Length
            If InStr(Query名, "*") >= 2 Then
                L = InStr(Query名, "*") - 1
                Query名 = Strings.Left(Query名, L)
            End If

            For Each viw As ADOX.View In cat.Views
                If Strings.Left(viw.Name, L) = Query名 Then
                    h.Add(viw.Name)
                End If
            Next

            For Each viewName As String In h
                ' ADODB.Connection 経由で DROP VIEW 実行（カタログ経由だと例外が出る環境があるため）
                cn.Execute("DROP VIEW [" & viewName & "]")
            Next
        Catch ex As Exception
            MessageBox.Show("DEL_QUERY エラー: " & ex.Message)
        Finally
            Try
                If cn IsNot Nothing AndAlso cn.State = 1 Then cn.Close()
            Catch
            End Try
            ReleaseCom(cat)
            ReleaseCom(cn)
            GC.Collect()
            GC.WaitForPendingFinalizers()
        End Try
    End Sub

    Public Sub DEL_table(ByVal Table名 As String)
        Dim cn As ADODB.Connection = Nothing
        Dim cat As ADOX.Catalog = Nothing
        If Table名 Is Nothing Then Return
        Dim a As String = If(Table名.Length >= 2, Table名.Substring(0, 2), Table名)
        If a = "MS" Or a = "ms" Then
            MsgBox("システムテーブルは削除不可")
            Return
        End If

        Try
            cn = New ADODB.Connection()
            cn.Open("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";")
            cat = New ADOX.Catalog()
            cat.let_ActiveConnection(cn)

            Dim L As Integer = If(Table名.Contains("*"), Table名.IndexOf("*"), Table名.Length)
            Dim prefix As String = Table名.Substring(0, L)

            ' 逆順で削除（インデックス変動対策）
            For i As Integer = cat.Tables.Count - 1 To 0 Step -1
                Dim tblName As String = cat.Tables(i).Name
                If tblName.StartsWith(prefix) Then
                    ' ADODB.Connection 経由で DROP TABLE 実行（安全）
                    cn.Execute("DROP TABLE [" & tblName & "]")
                End If
            Next
        Catch ex As Exception
            MessageBox.Show("DEL_table エラー: " & ex.Message)
        Finally
            Try
                If cn IsNot Nothing AndAlso cn.State = 1 Then cn.Close()
            Catch
            End Try
            ReleaseCom(cat)
            ReleaseCom(cn)
            GC.Collect()
            GC.WaitForPendingFinalizers()
        End Try
    End Sub

    Public Function Search_Table(ByVal Table名 As String) As Boolean
        Dim cn As ADODB.Connection = Nothing
        Dim cat As ADOX.Catalog = Nothing
        Try
            Dim a As String = If(Table名.Length >= 2, Table名.Substring(0, 2), Table名)
            If a = "MS" Or a = "ms" Then
                MsgBox("システムテーブルは対象外")
                Return False
            End If

            cn = New ADODB.Connection()
            cn.Open("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & MyDB & ";")
            cat = New ADOX.Catalog()
            cat.let_ActiveConnection(cn)

            Dim L As Integer = Table名.Length
            If a.Contains("*") Then
                If InStr(Table名, "*") >= 2 Then
                    L = InStr(Table名, "*") - 1
                    Table名 = Strings.Left(Table名, L)
                End If
                For i As Integer = 0 To cat.Tables.Count - 1
                    If Strings.Left(cat.Tables(i).Name, L) = Table名 Then
                        Return True
                    End If
                Next
            Else
                For i As Integer = 0 To cat.Tables.Count - 1
                    If Table名 = cat.Tables(i).Name Then
                        Return True
                    End If
                Next
            End If

            Return False
        Catch ex As Exception
            ' 検索エラーは false を返す
            Return False
        Finally
            Try
                If cn IsNot Nothing AndAlso cn.State = 1 Then cn.Close()
            Catch
            End Try
            ReleaseCom(cat)
            ReleaseCom(cn)
            GC.Collect()
            GC.WaitForPendingFinalizers()
        End Try
    End Function

#End Region
#Region "My_DATA関連”
    Public Sub My_DATAテーブル作成()
        DEL_table("My_DATA1")
        DEL_QUERY("QMy*")

        Dim 設定者 As String = "Tera"
        ケース出荷ランクキー = "Cケ"
        バラ出荷ランクキー = "B行"
        ランクA1 = 50
        ランクA2 = 20
        ランクB = 15
        ランクC = 10
        ランクD = 5

        ' StringBuilder使用で高速化
        Dim sb As New System.Text.StringBuilder()
        sb.Append("SELECT ")
        sb.Append("""" & 設定者 & """ As 設定者,")
        sb.Append("""" & 分析日 & """ As 分析日,")
        sb.Append("""" & ケース出荷ランクキー & """ As ケース出荷ランクキー,")
        sb.Append("""" & バラ出荷ランクキー & """ As バラ出荷ランクキー,")
        sb.Append(ランクA1 & " As ランクA1,")
        sb.Append(ランクA2 & " As ランクA2,")
        sb.Append(ランクB & " As ランクB,")
        sb.Append(ランクC & " As ランクC,")
        sb.Append(ランクD & " As ランクD ")
        sb.Append("INTO My_DATA1;")

        SQL実行(sb.ToString())
    End Sub

    Public Sub My_DATA読込()
        Dim dbCon As OleDbConnection
        Dim dbCmd As OleDbCommand
        dbCon = New OleDbConnection()
        Dim cst As String = ""
        cst &= "Provider=Microsoft.ACE.OLEDB.12.0;"
        cst = cst & "Data Source=" & MyDB
        dbCon.ConnectionString = cst
        dbCon.Open()

        Dim dtTbl As New DataTable
        dbCmd = New OleDbCommand("SELECT My_DATA1.* FROM My_DATA1;", dbCon)
        Dim dbAdp As New OleDbDataAdapter(dbCmd)
        dbAdp.Fill(dtTbl)

        K1.設定者 = dtTbl.Rows(0)("設定者").ToString()
        ケース出荷ランクキー = dtTbl.Rows(0)("ケース出荷ランクキー").ToString()
        バラ出荷ランクキー = dtTbl.Rows(0)("バラ出荷ランクキー").ToString()
        分析日 = dtTbl.Rows(0)("分析日").ToString()
        ランクA1 = CDbl(dtTbl.Rows(0)("ランクA1").ToString)
        ランクA2 = CDbl(dtTbl.Rows(0)("ランクA2").ToString)
        ランクB = CDbl(dtTbl.Rows(0)("ランクB").ToString)
        ランクC = CDbl(dtTbl.Rows(0)("ランクC").ToString)
        ランクD = CDbl(dtTbl.Rows(0)("ランクD").ToString)

        ' ACCESS DB クローズ
        dbCon.Close()
        dbCon.Dispose()
    End Sub



#End Region

#Region "T200から作成Tp"
    Private Sub テーブルアタッチBt_Click(sender As Object, e As EventArgs) Handles テーブルアタッチBt.Click

        Tera計算Data確認() ' ファイルパス確認
        メモ1Tb.Text = "Access新規作成..." : メモ1Tb.BackColor = Color.Yellow : Me.Refresh()


        Access新規作成() ' 新規DB作成
        メモ1Tb.Text = "テーブルコピー（10テーブル）を開始します..." : メモ1Tb.BackColor = Color.Yellow : Me.Refresh()

        ' 一括処理用のテーブルリスト
        Dim tables As String() = {
            "My_DATA1",
            "T110_区分換算",
            "T150_B単位出荷E集計",
            "T150_B単位出荷I集計",
            "T150_C単位出荷E集計",
            "T150_C単位出荷I集計",
            "T160_全出荷E集計",
            "T160_全出荷I集計",
            "T180_EIQランク",
            "T200"
        }

        ' 単一の Access インスタンスで TransferDatabase を実行（高速化）
        テーブルアタッチ複数(tables)

        Me.Invoke(Sub()
                      メモ2Tb.Text = "テーブルアタッチ 完了" : メモ2Tb.BackColor = Color.Yellow : Me.Refresh()
                  End Sub)

        メモ3Tb.Text = "乱数追加（6テーブル）を開始します..." : メモ1Tb.BackColor = Color.Yellow : Me.Refresh()

        乱数追加()

        Me.Close()
        MsgBox("Tera計算.accdbにアタッチ完了、必要なテーブル T200・その他 が利用可能")
    End Sub

    Private Sub テーブルアタッチ複数(tables As IEnumerable(Of String))
        Dim sourceDbPath As String = Tera計算DataPath & "\Tera計算.accdb"
        Dim targetDbPath As String = Tera計算DataPath & "\EIQ分析AI化\EIQ_AIモデル.accdb"

        Dim accessApp As Application = Nothing
        Try
            accessApp = New Application()
            accessApp.OpenCurrentDatabase(targetDbPath)

            For Each tableName As String In tables
                Me.Invoke(Sub()
                              メモ1Tb.Text = "アタッチ中: " & tableName : メモ1Tb.BackColor = Color.Yellow : Me.Refresh()
                          End Sub)

                Try
                    ' 既存テーブルがあれば削除
                    Try
                        accessApp.DoCmd.DeleteObject(AcObjectType.acTable, tableName)
                    Catch
                        ' 存在しない場合は無視
                    End Try

                    ' TransferDatabase でインポート（ソースからターゲットへコピー）
                    accessApp.DoCmd.TransferDatabase(
                        AcDataTransferType.acImport,
                        "Microsoft Access",
                        sourceDbPath,
                        AcObjectType.acTable,
                        tableName,
                        tableName
                    )

                    Me.Invoke(Sub()
                                  メモ1Tb.Text = "完了: " & tableName : メモ1Tb.BackColor = Color.LightGreen : Me.Refresh()
                              End Sub)

                Catch ex As Exception
                    ' TransferDatabase が失敗した場合は既存のフォールバックを使う
                    Me.Invoke(Sub()
                                  メモ1Tb.Text = $"TransferDatabase失敗({tableName}): {ex.Message} → フォールバック実行"
                                  メモ1Tb.BackColor = Color.Yellow
                                  Me.Refresh()
                              End Sub)
                    Try
                        テーブルアタッチ(tableName)
                    Catch ex2 As Exception
                        Me.Invoke(Sub()
                                      メモ1Tb.Text = "フォールバック失敗: " & tableName & " - " & ex2.Message
                                      メモ1Tb.BackColor = Color.LightCoral
                                      Me.Refresh()
                                  End Sub)
                    End Try
                End Try
            Next

        Catch ex As Exception
            MessageBox.Show("Access 一括アタッチエラー: " & ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
        Finally
            If accessApp IsNot Nothing Then
                Try
                    accessApp.CloseCurrentDatabase()
                    accessApp.Quit()
                Catch
                End Try
                ReleaseCom(accessApp)
            End If
        End Try
    End Sub
    Private Sub テーブルアタッチ(Mytebl As String)
        ' --- パス設定 ---
        Dim sourcePath As String = IO.Path.Combine(Tera計算DataPath, "Tera計算.accdb")
        Dim targetPath As String = IO.Path.Combine(Tera計算DataPath, "EIQ分析AI化", "EIQ_AIモデル.accdb")
        Dim tableName As String = Mytebl

        If Not IO.File.Exists(sourcePath) Then
            MessageBox.Show("ソースDBが見つかりません: " & sourcePath, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return
        End If
        If Not IO.File.Exists(targetPath) Then
            MessageBox.Show("ターゲットDBが見つかりません: " & targetPath, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return
        End If

        ' 接続文字列（インポート先をベースにする）
        Dim connString As String = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={targetPath};"

        Using conn As New OleDbConnection(connString)
            Try
                conn.Open()

                ' 1. 既存テーブルの削除（存在する場合のみ）
                Try
                    Using dropCmd As New OleDbCommand($"DROP TABLE [{tableName}]", conn)
                        dropCmd.ExecuteNonQuery()
                    End Using
                Catch
                    ' テーブルがない場合は無視して続行
                End Try

                ' 2. まず Access の TransferDatabase を試行（高速で安定する場合がある）
                Dim transferSucceeded As Boolean = False
                Try
                    テーブルアタッチ1(tableName)
                    transferSucceeded = True
                Catch ex As Exception
                    Me.Invoke(Sub()
                                  メモ1Tb.Text = $"TransferDatabase失敗({tableName}): {ex.Message} → SELECT INTOで再試行"
                                  メモ1Tb.BackColor = Color.Yellow
                                  Me.Refresh()
                              End Sub)
                End Try

                If Not transferSucceeded Then
                    ' 3. フォールバック: 高速インポートSQL
                    Dim sql As String = $"SELECT * INTO [{tableName}] FROM [{tableName}] IN '{sourcePath}'"
                    Using cmd As New OleDbCommand(sql, conn)
                        cmd.ExecuteNonQuery()
                    End Using
                End If

            Catch ex As Exception
                ' どちらの手法でも失敗した場合はユーザーに通知
                MessageBox.Show("テーブルコピー失敗: " & ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try
        End Using
    End Sub
    Private Sub テーブルアタッチ1(Mytebl As String)
        ' パスの設定
        Dim sourceDbPath As String = Tera計算DataPath & "\Tera計算.accdb"
        Dim targetDbPath As String = Tera計算DataPath & "\EIQ分析AI化\EIQ_AIモデル.accdb"
        Dim tableName As String = Mytebl
        ' Accessアプリケーションオブジェクトの作成
        Dim accessApp As New Application()

        Try
            ' インポート先のデータベースを開く
            accessApp.OpenCurrentDatabase(targetDbPath)

            ' データベース間インポートの実行
            ' 引数: インポート, 種類, ソースDBパス, オブジェクト種類, ソーステーブル名, ターゲット名
            accessApp.DoCmd.TransferDatabase(
                AcDataTransferType.acImport,
                "Microsoft Access",
                sourceDbPath,
                AcObjectType.acTable,
                tableName,
                tableName
            )

            'MessageBox.Show("インポートが完了しました。", "成功")

        Catch ex As Exception
            MessageBox.Show("エラーが発生しました: " & ex.Message, "エラー")
        Finally
            ' データベースを閉じてリソースを解放
            accessApp.CloseCurrentDatabase()
            accessApp.Quit()
            System.Runtime.InteropServices.Marshal.ReleaseComObject(accessApp)
        End Try
    End Sub

    Sub Accessアタッチ(tableName As String)
        ' パスの設定
        Dim sourceDbPath As String = Tera計算DataPath & "\Tera計算.accdb"
        Dim targetDbPath As String = Tera計算DataPath & "\EIQ分析AI化\EIQ_AIモデル.accdb"

        メモ1Tb.Text = "アタッチ中: " & tableName : メモ1Tb.BackColor = Color.Yellow : Me.Refresh()

        Dim cn As ADODB.Connection = Nothing
        Dim catalog As ADOX.Catalog = Nothing
        Dim newTable As ADOX.Table = Nothing

        Try
            cn = New ADODB.Connection()
            cn.Open("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & targetDbPath & ";")

            catalog = New ADOX.Catalog()
            catalog.let_ActiveConnection(cn)

            ' 既存の同名リンクテーブルがある場合は削除（ADOXカタログではなく ADODB 経由で削除）
            For i As Integer = catalog.Tables.Count - 1 To 0 Step -1
                If catalog.Tables(i).Name = tableName Then
                    cn.Execute("DROP TABLE [" & tableName & "]")
                    Exit For
                End If
            Next

            ' リンクテーブルの作成
            newTable = New ADOX.Table()
            newTable.Name = tableName

            ' ★修正：ParentCatalogを設定してからプロパティにアクセス
            newTable.ParentCatalog = catalog

            ' ★修正：プロパティの正しい設定方法
            With newTable.Properties
                .Item("Jet OLEDB:Create Link").Value = True
                .Item("Jet OLEDB:Link Provider String").Value = "MS Access"
                .Item("Jet OLEDB:Link Datasource").Value = sourceDbPath
                .Item("Jet OLEDB:Remote Table Name").Value = tableName
            End With

            ' カタログに追加
            catalog.Tables.Append(newTable)

            メモ1Tb.Text = "完了: " & tableName : メモ1Tb.BackColor = Color.LightGreen : Me.Refresh()

        Catch ex As Exception
            MsgBox("テーブルアタッチエラー [" & tableName & "]: " & vbCrLf & ex.Message, MsgBoxStyle.Critical)
            メモ1Tb.Text = "エラー: " & tableName : メモ1Tb.BackColor = Color.LightCoral
        Finally
            Try
                If cn IsNot Nothing AndAlso cn.State = 1 Then cn.Close()
            Catch
            End Try
            ReleaseCom(newTable)
            ReleaseCom(catalog)
            ReleaseCom(cn)
            GC.Collect()
            GC.WaitForPendingFinalizers()
        End Try
    End Sub




#End Region

#Region "計算ルール設定Tp"

    Private Sub レコード空白Bt_Click_1(sender As Object, e As EventArgs)
        '連番Cb.Textは保存キーとして残す
        設定者Tb.Text = ""
        設定日Tb.Text = ""
        ケース出荷Cb.Text = ""
        バラ出荷Cb.Text = ""
        ランクA1Tb.Text = ""
        ランクA2Tb.Text = ""
        ランクBTb.Text = ""
        ランクCTb.Text = ""
        ランクDTb.Text = ""
        'With MyData_Dgv

        'End With
    End Sub

    Private Sub 計算の設定値にするTb_Click(sender As Object, e As EventArgs)
        TpChk1 = 1
        If 連番Cb.Text = "1" Then
            Tera設定Rb.Checked = True
            ユーザー設定Cb.Checked = False

        Else
            ユーザー設定Cb.Checked = True
            Tera設定Rb.Checked = False
        End If

        画面Ct.SelectedTab = 出荷データから作成Tp
        S連番Tb.Text = 連番Cb.Text
        S設定者Tb.Text = 設定者Tb.Text
        S設定日Tb.Text = 設定日Tb.Text
        Sケース出荷Tb.Text = ケース出荷Cb.Text
        Sバラ出荷Tb.Text = バラ出荷Cb.Text
        SランクA1Tb.Text = ランクA1Tb.Text
        SランクA2Tb.Text = ランクA2Tb.Text
        SランクBTb.Text = ランクBTb.Text
        SランクCTb.Text = ランクCTb.Text
        SランクDTb.Text = ランクDTb.Text

        ケース出荷ランクキー = Sケース出荷Tb.Text
        バラ出荷ランクキー = Sバラ出荷Tb.Text
        ランクA1 = CDbl(SランクA1Tb.Text)
        ランクA2 = CDbl(SランクA2Tb.Text)
        ランクB = CDbl(SランクBTb.Text)
        ランクC = CDbl(SランクCTb.Text)
        ランクD = CDbl(SランクDTb.Text)


    End Sub

    Private Sub 連番Cb_SelectedIndexChanged(sender As Object, e As EventArgs)
        If TpChk1 = 1 Then Return
        If String.IsNullOrWhiteSpace(連番Cb.Text) Then Exit Sub

        If 連番Cb.Text = "1" Then
            設定者Tb.Text = "Tera設定"
            設定日Tb.Text = "2026/02/20"
            ケース出荷Cb.Text = "Cケ"
            バラ出荷Cb.Text = "B行"
            ランクA1Tb.Text = "50"
            ランクA2Tb.Text = "20"
            ランクBTb.Text = "15"
            ランクCTb.Text = "10"
            ランクDTb.Text = "5"
            変更削除表示L.Text = "変更削除 不可"
            Exit Sub
        End If

        ' 文字列を数値に変換して比較
        Dim selectedNum As Integer
        If Integer.TryParse(連番Cb.Text, selectedNum) Then
            If selectedNum >= 2 AndAlso selectedNum <= 5 Then
                For Each row As DataGridViewRow In MyData_Dgv.Rows
                    If row.IsNewRow Then Continue For
                    If row.Cells(0).Value IsNot Nothing AndAlso row.Cells(0).Value.ToString() = selectedNum.ToString() Then
                        設定者Tb.Text = If(row.Cells(1).Value IsNot Nothing, row.Cells(1).Value.ToString(), "")
                        設定日Tb.Text = If(row.Cells(2).Value IsNot Nothing, row.Cells(2).Value.ToString(), "")
                        ケース出荷Cb.Text = If(row.Cells(3).Value IsNot Nothing, row.Cells(3).Value.ToString(), "")
                        バラ出荷Cb.Text = If(row.Cells(4).Value IsNot Nothing, row.Cells(4).Value.ToString(), "")
                        ランクA1Tb.Text = If(row.Cells(5).Value IsNot Nothing, row.Cells(5).Value.ToString(), "")
                        ランクA2Tb.Text = If(row.Cells(6).Value IsNot Nothing, row.Cells(6).Value.ToString(), "")
                        ランクBTb.Text = If(row.Cells(7).Value IsNot Nothing, row.Cells(7).Value.ToString(), ""
                        )
                        ランクCTb.Text = If(row.Cells(8).Value IsNot Nothing, row.Cells(8).Value.ToString(), "")
                        ランクDTb.Text = If(row.Cells(9).Value IsNot Nothing, row.Cells(9).Value.ToString(), "")
                        変更削除表示L.Text = "変更削除 可"
                        Exit For
                    End If
                Next
            End If
        End If
    End Sub



    Private Sub 変更追加Tb_Click_1(sender As Object, e As EventArgs)
        ' 連番の値をチェック
        If String.IsNullOrWhiteSpace(連番Cb.Text) Then
            MsgBox("連番を選択してください。")
            Return
        End If

        Dim selectedNum As Integer
        If Not Integer.TryParse(連番Cb.Text, selectedNum) Then
            MsgBox("連番が正しくありません。")
            Return
        End If

        ' 入力値の検証を削除（空白も許可）
        ' If String.IsNullOrWhiteSpace(設定者Tb.Text) Then
        '     MsgBox("設定者を入力してください。")
        '     Return
        ' End If

        ' If String.IsNullOrWhiteSpace(設定日Tb.Text) Then
        '     MsgBox("設定日を入力してください。")
        '     Return
        ' End If

        ' ランク合計チェックも空白を考慮
        ' If Not ランクChk() Then
        '     MsgBox("ランク合計が100になるよう調整してください。")
        '     Return
        ' End If

        ' DataGridViewから該当行を探す
        Dim targetRow As DataGridViewRow = Nothing
        For Each row As DataGridViewRow In MyData_Dgv.Rows
            If row.IsNewRow Then Continue For
            If row.Cells(0).Value IsNot Nothing AndAlso row.Cells(0).Value.ToString() = selectedNum.ToString() Then
                targetRow = row
                Exit For
            End If
        Next

        ' データの準備（空白もそのまま設定）`
        Dim rowValues As Object() = {
            selectedNum,
            If(String.IsNullOrWhiteSpace(設定者Tb.Text), "", 設定者Tb.Text),
            If(String.IsNullOrWhiteSpace(設定日Tb.Text), "", 設定日Tb.Text),
            If(String.IsNullOrWhiteSpace(ケース出荷Cb.Text), "", ケース出荷Cb.Text),
            If(String.IsNullOrWhiteSpace(バラ出荷Cb.Text), "", バラ出荷Cb.Text),
            If(String.IsNullOrWhiteSpace(ランクA1Tb.Text), "", ランクA1Tb.Text),
            If(String.IsNullOrWhiteSpace(ランクA2Tb.Text), "", ランクA2Tb.Text),
            If(String.IsNullOrWhiteSpace(ランクBTb.Text), "", ランクBTb.Text),
            If(String.IsNullOrWhiteSpace(ランクCTb.Text), "", ランクCTb.Text),
            If(String.IsNullOrWhiteSpace(ランクDTb.Text), "", ランクDTb.Text)
        }

        If targetRow IsNot Nothing Then
            ' 既存行を更新
            For i As Integer = 0 To rowValues.Length - 1
                If i < MyData_Dgv.ColumnCount Then
                    targetRow.Cells(i).Value = rowValues(i)
                End If
            Next
            MsgBox($"連番 {selectedNum} のデータを更新しました。")
            変更削除表示L.Text = "変更 済"
        Else
            ' 新規行を追加
            Dim dt As DataTable = CType(MyData_Dgv.DataSource, DataTable)
            If dt IsNot Nothing Then
                dt.Rows.Add(rowValues)
                MsgBox($"連番 {selectedNum} のデータを追加しました。")
                変更削除表示L.Text = "追加 済"
            End If
        End If

        SaveToExcel()
    End Sub

    Private Sub LoadExcelDataSimple()
        Dim filePath As String = IO.Path.Combine(Tera計算DataPath, "EIQ分析AI化\EIQ_AIモデル設定.xlsx")

        If Not IO.File.Exists(filePath) Then
            MsgBox($"設定ファイルが見つかりません:{vbCrLf}{filePath}")
            Return
        End If

        Try
            MyData_Dgv.SuspendLayout()
            MyData_Dgv.DataSource = Nothing ' 先にクリア

            ' ClosedXMLで直接読み込み（OLEDBより高速）
            Using workbook As New XLWorkbook(filePath)
                Dim worksheet As IXLWorksheet = Nothing

                ' シート名 "Mydata" があればそれを使い、無ければ先頭シートを使う
                If workbook.Worksheets.Contains("Mydata") Then
                    worksheet = workbook.Worksheet("Mydata")
                ElseIf workbook.Worksheets.Count > 0 Then
                    worksheet = workbook.Worksheets.First()
                Else
                    MsgBox($"ワークブックにシートが存在しません:{vbCrLf}{filePath}")
                    Return
                End If

                Dim dt As New DataTable()

                ' ヘッダー行を取得
                Dim firstRow = worksheet.FirstRowUsed()
                If firstRow Is Nothing Then
                    MsgBox($"ワークシートにヘッダー行が見つかりません:{vbCrLf}{worksheet.Name}")
                    Return
                End If

                Dim headerCells = firstRow.CellsUsed()
                If headerCells Is Nothing OrElse headerCells.Count() = 0 Then
                    MsgBox($"ヘッダーセルが見つかりません:{vbCrLf}{worksheet.Name}")
                    Return
                End If

                ' ヘッダー追加
                For Each cell In headerCells
                    Dim cellVal As String = ""
                    Try
                        cellVal = cell.GetValue(Of String)()
                    Catch
                        Try
                            cellVal = If(cell.Value IsNot Nothing, cell.Value.ToString(), "")
                        Catch
                            cellVal = ""
                        End Try
                    End Try
                    Dim colName As String = If(String.IsNullOrWhiteSpace(cellVal), "Column" & dt.Columns.Count.ToString(), cellVal.Trim())
                    dt.Columns.Add(colName)
                Next

                ' データ追加: ヘッダー行より下の使用行を読み取る
                For Each row In worksheet.RowsUsed()
                    If row.RowNumber() <= firstRow.RowNumber() Then Continue For
                    Dim dr = dt.NewRow()
                    For i = 0 To dt.Columns.Count - 1
                        Try
                            Dim cell = row.Cell(i + 1)
                            Dim cellVal As String = ""
                            If Not cell.IsEmpty() Then
                                Try
                                    cellVal = cell.GetValue(Of String)()
                                Catch
                                    cellVal = If(cell.Value IsNot Nothing, cell.Value.ToString(), "")
                                End Try
                            End If
                            dr(i) = cellVal
                        Catch
                            dr(i) = ""
                        End Try
                    Next
                    dt.Rows.Add(dr)
                Next

                MyData_Dgv.DataSource = dt
            End Using

            Dgv書式(MyData_Dgv)
            メモTb.Text = $"ユーザー設定チェックで計算ルール変更可、出荷データから作成を押す "
            メモTb.BackColor = Color.Yellow
        Catch ex As Exception
            MsgBox($"Excel読み込みエラー:{vbCrLf}{ex.Message}")
        Finally
            MyData_Dgv.ResumeLayout()
        End Try
    End Sub

    Private Function ランクChk() As Boolean
        Try
            Dim A1 As Double = CDbl(ランクA1Tb.Text)
            Dim A2 As Double = CDbl(ランクA2Tb.Text)
            Dim B As Double = CDbl(ランクBTb.Text)
            Dim C As Double = CDbl(ランクCTb.Text)
            Dim D As Double = CDbl(ランクDTb.Text)
            Dim 合計 As Double = A1 + A2 + B + C + D

            Return 合計 = 100
        Catch ex As Exception
            MsgBox("ランク値の入力エラー: " & ex.Message)
            Return False
        End Try
    End Function

    Private Sub Dgv書式(dgv As DataGridView)
        dgv.SuspendLayout()
        Try
            With dgv
                .RowHeadersVisible = False
                .AllowUserToAddRows = True
                .AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None ' AllCells は遅い
                .AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None
                .ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter

                ' 列幅を手動設定（高速化）
                If .ColumnCount > 0 Then .Columns(0).Width = 60
                For j = 1 To .ColumnCount - 1
                    .Columns(j).DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                    .Columns(j).Width = 100
                Next

                If .ColumnCount > 2 Then
                    .Columns(2).DefaultCellStyle.Format = "yyyy/MM/dd"
                    .Columns(2).Width = 120
                End If
            End With
        Finally
            dgv.ResumeLayout()
        End Try
    End Sub

    Private Sub SaveToExcel()
        Dim filePath As String = IO.Path.Combine(Tera計算DataPath, "EIQ分析AI化\EIQ_AIモデル設定.xlsx")

        Try
            If Not IO.File.Exists(filePath) Then
                MsgBox("Excelファイルが見つかりません: " & filePath)
                Return
            End If

            Using workbook As New XLWorkbook(filePath)
                Dim worksheet As IXLWorksheet = workbook.Worksheet("Mydata")
                Dim dt As DataTable = CType(MyData_Dgv.DataSource, DataTable)

                If dt IsNot Nothing Then
                    ' 既存データをクリア
                    Dim lastRowUsed = worksheet.LastRowUsed()
                    Dim lastRow As Integer = If(lastRowUsed IsNot Nothing, lastRowUsed.RowNumber(), 1)

                    If lastRow > 1 Then
                        Dim lastColumnUsed = worksheet.LastColumnUsed()
                        Dim lastColumn As Integer = If(lastColumnUsed IsNot Nothing, lastColumnUsed.ColumnNumber(), 1)
                        worksheet.Range(2, 1, lastRow, lastColumn).Clear()
                    End If

                    ' データを書き込み
                    For i As Integer = 0 To dt.Rows.Count - 1
                        For j As Integer = 0 To dt.Columns.Count - 1
                            worksheet.Cell(i + 2, j + 1).Value = If(dt.Rows(i)(j) Is DBNull.Value, "", dt.Rows(i)(j).ToString())
                        Next
                    Next

                    workbook.Save()
                    Access新規作成メモTb.Text = "計算ルール保存完了"
                    Access新規作成メモTb.BackColor = Color.LightGreen
                End If
            End Using
        Catch ex As Exception
            MsgBox("保存エラー: " & ex.Message)
            Access新規作成メモTb.Text = "保存失敗"
            Access新規作成メモTb.BackColor = Color.LightCoral
        End Try
    End Sub










#End Region
#End Region

End Class
